/**
 * @file ngserver.cpp
 * @author Suk Robert Koh (srkoh87z@gmail.com)
 */

#include "ngine/server/ngserver.h"

/**
 * Default constructor for NGServer class
 */
NGServer::NGServer(int _maxUser)
{
	int i;
	maxUser = _maxUser;
	currentUserId = 1;

	// initialize users
	users = (NGUser **) malloc(sizeof(NGUser *) * maxUser);
	for(i = 0; i < maxUser; i++)
		users[i] = new NGUser();

	// initialize message queue
	messageQueue = new NGMessageQueue();

	// use default message coder for now
	messageCoder = new NGDefaultMessageCoder();

	NGLogger::debug("NGServer constructor() finished");
}

/**
 * Destructor for NGServer class
 */
NGServer::~NGServer()
{
	int i;

	for(i = 0; i < maxUser; i++)
	{
		// close socket if user is connected
		if(users[i]->connected)
			closesocket(users[i]->socket);

		free(users[i]);
	}

	free(users);
}

/**
 * Sends a message to a user
 *
 * @param	NGMessage *		Pointer to the message to be sent
 * @param	DWORD			Receiving user's ID
 * @return	BOOL			Whether or not it was successful
 *
 * TODO: make use of BOOL return
 */
BOOL NGServer::sendMessage(NGMessage *msg, DWORD userId)
{
	int i;

	for(i = 0; i < maxUser; i++)
	{
		// find user and put message into its send queue
		if(users[i]->connected && users[i]->userId == userId)
		{
			users[i]->sendQueue->push(msg);
			return TRUE;
		}
	}

	return FALSE;
}

/**
 * Sends message to all users currently connected
 *
 * @param	NGMessage *		Pointer to the message to be sent
 * @return	BOOL			Whether or not it was successful
 *
 * TODO: make use of BOOL return
 */
BOOL NGServer::sendMessageAll(NGMessage *msg)
{
	int i;

	for(i = 0; i < maxUser; i++)
	{
		// put message to send queue for all users
		if(users[i]->connected)
		{
			users[i]->sendQueue->push(msg);
		}
	}

	return FALSE;
}

/**
 * Sends message to all users currently connected except the one specified by excludeUserId
 *
 * @param	NGMessage *		Pointer to the message to be sent
 * @param	DWORD			User ID of the user to be excluded
 * @return	BOOL			Whether or not it was successfull
 *
 * TODO: make use of BOOL return
 */
BOOL NGServer::sendMessageAll(NGMessage *msg, DWORD excludeUserId)
{
	int i;

	for(i = 0; i < maxUser; i++)
	{
		// put the mssage into send queue if user ID does not match excludeUserId
		if(users[i]->connected && users[i]->userId != excludeUserId)
		{
			users[i]->sendQueue->push(msg);
		}
	}

	return FALSE;
}

/**
 * Sets/replaces the message coder to be used for encoding/decoding messages
 *
 * @param	NGMessageCoder *	Pointer to the message coder to be used
 */
void NGServer::setMessageCoder(NGMessageCoder *_messageCoder)
{
	// release if messageCoder already points to an object
	if(messageCoder)
		free(messageCoder);

	messageCoder = _messageCoder;
}

/**
 * Gets the size of the message queue
 *
 * @return	int		Size of the message queue
 */
int NGServer::getQueueSize()
{
	return messageQueue->size();
}

/**
 * Gets the first message of the message queue
 *
 * @return	NGMessage *		Pointer to the first message of the message queue
 */
NGMessage *NGServer::getFirstMessage()
{
	return messageQueue->pop();
}

/**
 * Gets an empty user space within users list
 *
 * @return	NGUser *	Pointer to an empty user space
 */
NGUser *NGServer::getEmptyUserSpace(void)
{
	int i;

	for(i = 0; i < maxUser; i++)
	{
		if(!users[i]->connected)
			return users[i];
	}

	return NULL;
}

/**
 * Gets a user object by userId
 *
 * @param	DWORD		User's ID to search for
 * @return	NGUser *	Pointer of the user found
 */
NGUser *NGServer::getUser(DWORD userId)
{
	int i;

	for(i = 0; i < maxUser; i++)
	{
		if(users[i]->userId == userId)
			return users[i];
	}

	return NULL;
}

/**
 * Gets a user object by socket
 *
 * @param	SOCKET		User's socket to search for
 * @return	NGUser *	Pointer of the user found
 *
 * TODO: determine if this is necessary
 */
NGUser *NGServer::getUser(SOCKET socket)
{
	int i;

	for(i = 0; i < maxUser; i++)
	{
		if(users[i]->socket == socket)
			return users[i];
	}

	return NULL;
}

/**
 * Removes a user by userId
 *
 * @param	DWORD	ID of a user to be removed
 * @return	BOOL	Whether or not it was successful
 *
 * TODO: make use of BOOL return
 */
BOOL NGServer::removeUser(DWORD userId)
{
	// search user by id
	NGUser *user = getUser(userId);

	// return false if user was not found
	if(user == NULL)
		return FALSE;

	// close user's socket
	closesocket(user->socket);

	// send up a message to let application know a user disconnected
	NGMessage *msg = new NGMessage();
	msg->setSenderId(user->userId);
	msg->setType(NGINE_MSG_DISCONNECT);
	msg->setData("");
	messageQueue->push(msg);

	// unset variables
	user->socket = INVALID_SOCKET;
	user->connected = FALSE;
	user->userId = 0;

	NGLogger::info("User disconnected: %lu", userId);

	return TRUE;
}
